"""
高阶函数
用函数构建抽象
Generalization
"""
from math import pi, sqrt


# 假设我们现在要求三个形状的面积，有如下三个函数的方法


def area_square(r):
    return r * r


def area_circle(r):
    return r * r * pi


def area_hexagon(r):
    """计算正六边形的面积"""
    return r * r * hexagon_constant


"""
假如我们每个函数都要进行断言，因为半径的长度不能为负数，所以我们对每个函数都要进行相同的断言
这样是比较麻烦的，所以我们应该想办法，将三个函数相同的部分抽象出来，形成一个新的函数
这就是抽象的意义
"""


def area(r, shape_constant):
    """计算面积, 断言：后面的语句如果断言判断为false，那么会抛出断言错误，内容为这段文字"""
    assert r > 0, 'the radius must be positive'
    return r * r * shape_constant


square = area(10, 1)
circle = area(10, pi)
hexagon_constant = 3 * sqrt(3) / 2
hexagon = area(10, hexagon_constant)


"""一些有着通用结构的函数，可能是一个计算过程，而不仅仅是一个数字"""


def sum_naturals(n):
    """
    计算从1到n的累加和
    >>> sum_naturals(5)
    15
    """
    total, k = 0, 1
    while k <= n:
        total, k = total + k, k + 1
    return total

        
def sum_cube(n):
    """
    计算n的立方和
    >>> sum_cube(5)
    225
    """
    total, k = 0, 1
    while k <= n:
        total, k = total + pow(k, 3), k + 1
    return total


"""
仔细观察上面两个函数，发现他们几乎完全一样，不一样的地方我们就可以单独
拿出来作为参数传进来，但是和常量不一样的是，这个部分是某个数字的运算后的结果
所以在python中，高阶函数允许接收一个函数作为参数，这样就可以更加方便的构建更加
高级的抽象
"""      


def identity(k):
    return k


def cube(k):
    return pow(k, 3)


def summation(n, term):
    """
    使用高阶函数中的函数作为参数，来更加深层次的构建抽象
    这样就实现了将两个求和函数构建成了一个求和函数。代码量更少了
    >>> summation(5, cube)
    225
    """
    total, k = 0, 1
    while k <= n:
        total, k = total + term(k), k + 1
    return total


def maker_adder(n):
    """
    函数作为返回值, 返回的结果是 return n + k
    函数的结构是，函数体内有一个内部函数，然后返回这个函数名
    >>> adder = maker_adder(3)
    >>> adder(4)
    7
    """
    def adder_three(k):
        return k + n

    return adder_three
